/***************************************************************************
 *   Copyright (C) 2007 by ipQalc team                                     *
 *   milambert@c-webhosting.org                                            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <QStringList>

#include "ipadress.h"
#include <math.h>

//definition 256^2 pour calcul ip
#define CONST 65536 

//constructeur & destructeur
IpAdress::IpAdress(QString ipAdress){
    this->ipAdress = ipAdress;
}

IpAdress::~IpAdress(){};

//methodes
char IpAdress::classAdr(){
    QChar qCl = (getClassAdr()).at(0);
    return qCl.toAscii();
}

bool IpAdress::isValid(){ //on valide l'ip donnée
// qDebug("is valid");
    QStringList list = this->ipAdress.split(".");
    int i;

    for(i=0; i<4; i++){
        if (list[i].toInt() > 255)
            return false;
    }
    return true;
}

bool IpAdress::isValid(QString mask){ //on valide le mask
// qDebug("is valid for mask");
    QStringList list = mask.split(".");
    int i, octet;
    bool check = false;

    for(i=0; i<4; i++){
        octet = list[i].toInt();
        if(octet == 0 || octet == 128 || octet == 192 || octet == 224 || octet == 240 ||
            octet == 248 || octet == 252 || octet == 254 || octet == 255){
            if(octet == 0)
                check = true;
            if(check && octet != 0)
                break;
        }
        else
            return false;
    }

    switch (this->classAdr()){
        case 'A':
            if (list[0].toInt() == 255)
                return true;
            break;
        case 'B':
            if (list[0].toInt() == 255 && list[1].toInt() == 255)
                return true;
            break;
        case 'C':
            if (list[0].toInt() == 255 && list[1].toInt() == 255 && list[2].toInt() == 255)
                return true;
            break;
        default:
            return false;
    }

    //default value (avoid a warning)
    return false;
}

QList<int> IpAdress::convertBin(){
// qDebug("convert bin");
    QStringList list = this->ipAdress.split(".");
    QList<int> adressBin;
    int i, j, nb;

    // intialise adressBin to 0
    for (i = 0; i < 32; i++){
        adressBin << 0;
    }

    for (i = 0; i < 4; i++){
        nb = list[i].toInt();
        for (j = ((i + 1) * 8) - 1; nb != 0; j--){
            adressBin[j] = (nb % 2);
            nb /= 2;
        }
    }
    return adressBin;
}

QString IpAdress::convertDeci(QList<int> adressBin){
// qDebug("convert deci");
    QString adress;
    QStringList strList;
    int i, j, k;
    double adrTmp;

    for (i = 0; i < 4; i++){
        adrTmp = 0;

        //la conversion se fait en inverse (Argl) depuis le dernier octet => c'a donne ce ,machin incomprehensible :D
        for (k = 0, j = (((i + 1) * 8) - 1); j != (((i + 1) * 8) - 9); k++, j--){
            adrTmp += (adressBin[j]) * (pow(2, k));
        }
        adress.setNum(adrTmp, 'g', 10);
        strList.append(adress);
    }
    return strList.join(".");
}

int IpAdress::nbBits(QString deci){
// qDebug("nbBits");
    unsigned int nbr = deci.toUInt();
    int cpt;
    for (cpt = 0; nbr != 0; cpt++){
        nbr /= 2;
    }
    return cpt;
}

int IpAdress::nbBitsClSn(QString nbrClSn){
// qDebug("nbBitsClSn");
    int bits = nbBits(nbrClSn);

    switch(this->classAdr()){
        case 'A':
            return 24 - bits; //max 24 bits libres dans un classs A
            break;
        case 'B':
            return 16 - bits; //max 16 bits libres dans un classe B
            break;
        case 'C':
            return 8 - bits; //max 8 bits libres dans une classe C
            break;
        default:
            return 0;
            break;
    }
    return 0;
}

QString IpAdress::getClassAdr(){
// qDebug("classAdr")
    QStringList list = this->ipAdress.split(".");
    int octet = list[0].toInt();

    if (octet < 127)
        return "A";
    else if (octet == 127)
        return "Localhost";
    else if (octet > 127 && octet <= 191)
        return "B";
    else if (octet > 191 && octet <= 223)
        return "C";
    else if (octet > 223 && octet <= 239)
        return "D";
    else if (octet > 239 && octet <= 255)
        return "E";
    else
        return "Error";

    return "Error";
}

QString IpAdress::getIpAdress(QString numCl, QString numSn, QString nbMax, bool maxiCl){
// qDebug("get Ip");
    QStringList retour = this->ipAdress.split(".");
    QString tmp;
    int bitsNumCl, oct2, oct3, oct4;
    double pax, pif = 0, paf = 0, numSub, numHost;

    if (maxiCl)
        bitsNumCl = nbBitsClSn(nbMax);
    else
        bitsNumCl = nbBits(nbMax);

    numSub = numSn.toDouble();
    numHost = numCl.toDouble();

    //la fonction est a un calul mathématique donné par le prof de labo
    pax = ((numSub * pow(2, bitsNumCl)) + numHost); // /!\Semble corecte, erreurs d'arondits possible :(

    switch (this->classAdr()){
        case 'A':
            pif = pax / CONST;
            oct2 = (int) pif;
            tmp.setNum(oct2);
            retour.replace(1, tmp);
            paf = pif - oct2;
            paf *= 256;
            oct3 = (int) paf;
            tmp.setNum(oct3);
            retour.replace(2, tmp);
            paf -= oct3;
            paf *= 256;
            oct4 = (int) paf;
            tmp.setNum(oct4);
            retour.replace(3, tmp);
            return retour.join(".");
            break;
        case 'B':
            pif = pax / CONST;
            oct2 = (int) pif;
            paf = pif - oct2;
            paf *= 256;
            oct3 = (int) paf;
            tmp.setNum(oct3);
            retour.replace(2, tmp);
            paf -= oct3;
            paf *= 256;
            oct4 = (int) paf;
            tmp.setNum(oct4);
            retour.replace(3, tmp);
            return retour.join(".");
            break;
        case 'C':
             pif = pax / CONST;
            oct2 = (int) pif;
            paf = pif - oct2;
            paf *= 256;
            oct3 = (int) paf;
            paf -= oct3;
            paf *= 256;
            oct4 = (int) paf;
            tmp.setNum(oct4);
            retour.replace(3, tmp);
            return retour.join(".");
            break;
    }
    return "ERROR";
}

QString IpAdress::getBroadcast(QString numSn, QString nbMax, bool maxiCl){
// qDebug("get broadcast");
    int nbBit, test;
    QString tmp;

    if (maxiCl)
        nbBit = nbBitsClSn(nbMax);
    else
        nbBit = nbBits(nbMax);

    //calcul du dernier cleint d'un sous-reseau en fonction de la premiere adresse du SR superieur
    test = (int) (pow(2,nbBit+1) - pow(2,nbBit)) - 1;

    tmp.setNum(test);
    return getIpAdress(tmp ,numSn,nbMax, maxiCl);
}

QString IpAdress::getMask(QString nbMax, bool maxiCl){
// qDebug("get mask");
    int nbBit, i;
    QList<int> maskBin;

    // init of binary nask
    for (i = 0; i < 32; i++){
        maskBin.append(0);
    }

    //get nb bits
    if (!maxiCl)
        nbBit = nbBitsClSn(nbMax);
    else
        nbBit = nbBits(nbMax);

    switch (this->classAdr()){
        case 'A':
            for (i = 0; i != (8 + nbBit); i++){
                maskBin[i] = 1;
            }
            break;
        case 'B':
            for (i = 0; i != (16 + nbBit); i++){
                maskBin[i] = 1;
            }
            break;
        case 'C':
            for (i = 0; i != (24 + nbBit); i++){
                maskBin[i] = 1;
            }
            break;
        default :
            return "Error";
    }
    return convertDeci(maskBin);
}

QString IpAdress::getNbHost(){
// qDebug("getNbHost");
    QList<int> adrBin = this->convertBin();
    int i, cpt = 0;
    double nb = 0;
    bool test = false;
    QString result, str;

    if (this->classAdr() == 'A' || this->classAdr() == 'B' || this->classAdr() == 'C'){
        for (i = 0; i < 32; i++) {
            if (adrBin[i] == 0) {
                nb += pow(2, cpt);
                cpt++;
            }
        }

        if (!(cpt == 0 || cpt == 1)){
            switch (this->classAdr()){
                case 'A':
                    if (!(cpt == 23 || cpt == 24))
                        test = true;
                    break;
                case 'B':
                    if (!(cpt == 15 || cpt == 16))
                        test = true;
                    break;
                case 'C':
                    if (!(cpt == 7 || cpt == 8))
                        test = true;
                    break;
            }

            if (test){
                nb -= 1;
                str.setNum((nb + 2), 'g', 10);
                result.append(str);
                result.append(" / ");
                str.setNum(nb, 'g', 10);
                return result.append(str);
            } else
                return "N/A";
        } else
            return "N/A";
    } else
        return "N/A";

    return "Error";
}

QString IpAdress::getNbSubnet(){
// qDebug("getNbSubnet");
    QList<int> adrBin = convertBin();
    int i, cpt = 0;
    double nb;
    QString str, result;

    if (this->classAdr() == 'A' || this->classAdr() == 'B' || this->classAdr() == 'C'){
        switch (this->classAdr()){
            case 'A':
                for (i = 8; i < 32; i++){
                    if (adrBin[i] == 1)
                        cpt++;
                }
                break;
            case 'B':
                for (i = 16; i < 32; i++){
                    if (adrBin[i] == 1)
                        cpt++;
                }
                break;
            case 'C':
                for (i = 24; i < 32; i++){
                    if (adrBin[i] == 1)
                        cpt++;
                }
                break;
        }

        if (!(cpt == 0 || cpt == 1)){
            nb = pow(2, cpt);
            str.setNum(nb, 'g', 10);
            result.append(str);
            result.append(" / ");
            str.setNum((nb - 2), 'g', 10);
            return result.append(str);
        } else
            return "N/A";
    } else
        return "N/A";

        return "Error";
}

QString IpAdress::getNetAdress(QString mask){
// qDebug("netAdr");
    QStringList adr = this->ipAdress.split(".");
    QStringList mak = mask.split(".");
    QStringList net;
    QString str;

    for (int i=0; i<4; i++){
        net.append(str.setNum(((adr[i].toInt()) & (mak[i].toInt())), 10));
    }
    return net.join(".");
}
